

import java.io.IOException;


import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import IL.ClassGenerator;
import IL.ILUtility;
import IL.MethodGenerator;
import ILTypes.ILClass;
import ILTypes.ILMethod;

import static org.objectweb.asm.Opcodes.*;

public class MyMethodAdapter extends MethodAdapter
{
	int prevIns;
	int argumentCount;
	int totalVarCount;
	String opcode_array[];	
	StringBuffer localVariableList;
	
	// ILGenerator iLGen;
	ILClass iCl;
	ILMethod iMl;
	MethodGenerator mg;
    ClassGenerator cg;
	
	MyMethodAdapter( MethodVisitor mv,
	  		int access, 
	  		String methName, 
	  		String methDesc,
	  		String methSign,
	  		ClassGenerator cG) 
    {
       super(mv);
       
       // this.iLGen = iLGen;
       this.cg = cG;
       opcode_array = new Opcode().GetOpcodes();
       localVariableList = new StringBuffer();

       Type[] paramTypes = Type.getArgumentTypes(methDesc);
       iMl = new ILMethod(access, methName, methDesc, methSign, paramTypes);
       mg = new MethodGenerator(iMl);
       
       iMl.AddPrintInstructions("===============================================");
       iMl.AddPrintInstructions("===============================================");
		
       iMl.AddPrintInstructions("Method Name :"+methName);
       System.out.println("Method Name :"+methName);
       iMl.AddPrintInstructions("Access code :"+access);
       if(methSign != null)
    	   iMl.AddPrintInstructions("Method Signature :"+methSign);
       iMl.AddPrintInstructions("Method Description :"+methDesc);
       iMl.AddPrintInstructions("+++++++++++++++++++++++++++++");
       
       for(int i=0;i<paramTypes.length;i++) {
    	   iMl.AddPrintInstructions("Parameter: " + (i+1) + " // Type: " + paramTypes[0].toString());
    	   //System.out.println("Parameter: " + (i+1) + " // Type: " + paramTypes[0].toString());
    	   
       }
    }
		
	
	public void visitCode() { // DONE
	  	
		super.visitCode();
	  	
		System.out.println("Visiting method body:"+iMl.GetName());
		iMl.AddPrintInstructions("Visiting method body:");
		iMl.AddPrintInstructions("----------------------");
		
		// iLGen.visitMethod(access,methodName,methodDescription,paramTypes,methodNumber); 
		mg.visitMethod(); 
	}
	
	 
	/*
	 *  Visits an instruction with a single int operand
	 * 
	 */
	@Override
	  public void visitInsn(int opcode) { //DONE
		iMl.AddPrintInstructions(opcode_array[opcode]);
		
		super.visitInsn(opcode);
		
		
		try {
			if(!(opcode==DUP && prevIns==NEW))
					mg.toCorrespondingIL(opcode_array[opcode]);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		prevIns = opcode;
	}
	
	
	
	@Override
	  public void visitJumpInsn(int opcode, Label label) { //DONE
		iMl.AddPrintInstructions(" : "+opcode_array[opcode]+" "+label);
		super.visitJumpInsn(opcode, label);
	  
		mg.toCorrespondingJumpIL(opcode,opcode_array[opcode], label.toString());
		prevIns = opcode;
	}
	
	@Override
	public void visitIntInsn(int opcode, int operand) { //DONE
		iMl.AddPrintInstructions(" : "+opcode_array[opcode]+" -- "+operand);
		super.visitIntInsn(opcode, operand);
		
		
		
		try {
			mg.toCorrespondingIL(opcode_array[opcode],operand);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		prevIns = opcode;
	}
	
	@Override
	public void visitVarInsn(int opcode, int var) { //DONE
		System.out.println(" : "+opcode_array[opcode]+"_"+var);
		super.visitVarInsn(opcode, var);
		//mg.toCorrespondingIL(new String(opcode_array[opcode]+"_"+var));
		System.out.println(opcode_array[opcode]+"----"+var);
		mg.dealLoadStoreInstructions(opcode,var);
		prevIns = opcode;
	}
	
	
	@Override
	public void visitFieldInsn(int opcode, String owner, String name, String desc) {//DONE
		iMl.AddPrintInstructions(" : "+opcode_array[opcode]+" Field Name:"+name+" ---> ** "+owner+" ---"+desc);
		super.visitFieldInsn(opcode, owner, name, desc);
		prevIns = opcode;
	}
	
	@Override
	public void visitLabel(Label label) {//DONE
	  	super.visitLabel(label);
	  	iMl.AddPrintInstructions("LABEL : "+label);
	  	mg.maintainLabels(label.toString());
	  	prevIns = 300; // That means it was a label
	}
	
	@Override
	public void visitLdcInsn(Object constant) {//DONE
		iMl.AddPrintInstructions(" : ldc " + constant);
		
		super.visitLdcInsn(constant);
		
		try {
			
			mg.toCorrespondingIL("ldc",constant.toString());
		
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		prevIns = 301;// That means that was a LDC instruction
	}

	@Override
	public void visitMethodInsn(int opcode, String owner, String name, String desc) { //DONE
	  	
		iMl.AddPrintInstructions(" : "+opcode_array[opcode]+" // Method :"+owner+"."+name+":"+desc);
	  	// System.out.println(" : "+opcode_array[opcode]+" // Method :"+owner+"."+name+":::"+desc);
	  	
	  	mg.doMethodCall(opcode, owner, name, desc, cg.cls.GetName());
	  	prevIns = opcode;
	}

	@Override
	public void visitIincInsn(int i, int value) {//DONE
	  	iMl.AddPrintInstructions(" : IINC "+i+ " by " +value);
	  	super.visitIincInsn(i, value);
	  	mg.toIncInstruction(i,value);
	  	
	  	prevIns = 302; // That means IINC
	}
	  
	
	@Override
	public void visitMultiANewArrayInsn(String type, int nrDimensions) { //DONE
		super.visitMultiANewArrayInsn(type, nrDimensions);
		mg.convertArrayInsn(type, nrDimensions);
		prevIns = 303;// THat means multinewarray
	}
	
	
	@Override
	public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
		super.visitTableSwitchInsn(min, max, dflt, labels);  // user ByteCode instruction
		iMl.AddPrintInstructions(" : "+"tableSwitch");
		prevIns = 304;//That means TableSwitch
	}
	 
	
	@Override
	public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
		iMl.AddPrintInstructions(" : "+"Look UP Switch..");
		super.visitLookupSwitchInsn(dflt, keys, labels); 
		prevIns = 305;//That means LookUpSwitch
	}

	
	@Override
	public void visitTypeInsn(int opcode, String type) {
		iMl.AddPrintInstructions(" : "+"ins:"+opcode_array[opcode]+" Type:"+type);
		//System.out.println(" : "+"ins:"+opcode_array[opcode]+" Type:"+type);
		
		super.visitTypeInsn(opcode, type);
		mg.doTypeInsConvertion(opcode,type);
		prevIns = opcode;
	}

	
	@Override  
	public void visitLineNumber(int line, Label start) {//DONE
		iMl.AddPrintInstructions(line+":"+" Label:"+start);
		super.visitLineNumber(line, start);
		mg.maintainLabels(start.toString());
		prevIns = 306; // Line number ins
	}
	
	@Override
	  public void visitMaxs(int maxStack, int maxLocals) {
		
		String variables="";
		
		int last = localVariableList.lastIndexOf(",");
		
		if(last!=-1)
			variables = localVariableList.substring(0, last);
		else
			variables = localVariableList.toString();
		
		
		mg.setStackAndLocalVariableDirective(maxStack,variables,totalVarCount,argumentCount);
	   
	  	super.visitMaxs(maxStack, maxLocals);
	  	
	  	
	  }
	
	 @Override
	  public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
	  	
	  	super.visitTryCatchBlock(start, end, handler, type);
	  }
	 
	 @Override
	 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) 
	 {
		 
		String ilType="";
		int i,first,last;
		totalVarCount++;
		
		if(name.equals("args"))
			 argumentCount++;
		
		else if(!name.equals("this"))
		{ 
			 ilType = ILUtility.resolveShortType(desc);
			 localVariableList.append(ilType);
		 
			 first = desc.indexOf("[");
			 last  = desc.lastIndexOf("["); 
			 
			 if(first!=-1) {
				 
				localVariableList.append("["); 
				for(i=first;i<=last;i++) {
					if(i<last)
						localVariableList.append("0...,");
					else
						localVariableList.append("0...");
				}
				localVariableList.append("]");  
			 } 
			 if(!ilType.equals(""))
				 localVariableList.append(",");  
		 }
		 
		 System.out.println();
		 System.out.println("_______________________________"+localVariableList);
		 System.out.println();
		 
		 
	 }

	 
	 @Override
	 public void visitEnd()
	 {
		 argumentCount=0;
		 mg.endMethodVisit();
		 super.visitEnd();
		 
		 cg.methodGeneratorList.add(mg);
		 
	 }
	
}